using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MatchCore;
using Net.Messgae;
using pbc = global::Google.Protobuf.Collections;

public class TurnManager
{
    public static readonly int PLAY_GAME_TICK = 1000 / 4 / Config.GAME_PER_SEC_FRAME_NUM;

    private bool _started = false;
    private bool _isPlaying = false;
    private bool _isWaiting = false;

    private long _updateFrameTime = 0;
    private uint _maxRecvFrameId = 0;
    private int _playGameTick = PLAY_GAME_TICK;

    private long _startTime = 0;

    private long _lastRecvFrameTime = 0;

    private long _lastUpdateFrameTime = 0;

    private List<FrameData> _turns = new List<FrameData>();

    public delegate void OnPlayStarted();
    public delegate void OnPlayEnd();

    public delegate void OnWaiting();
    public delegate void OnWaitEnd();

    public OnPlayStarted onPlayStarted;
    public OnPlayEnd onPlayEnd;
    public OnWaiting onWaiting;
    public OnWaitEnd onWaitEnd;


    public TurnManager()
    {

    }

    static private TurnManager sTurnManager = null;
    public static TurnManager Instance()
    {
        if(null == sTurnManager)
        {
            sTurnManager = new TurnManager();
        }
        return sTurnManager;
    }

    public static void Destroy()
    {
        sTurnManager = null;

        LogicManager.Destroy();
    }

    public bool IsRunning()
    {
        return !_isPlaying && !_isWaiting;
    }

    public void SetPlayGameTick(int tick)
    {
        _playGameTick = tick;
    }

    public void SetStartTime(long time)
    {
        _startTime = time;
    }        

    public bool Start()
    {
        if(_started)
        {
            return false;
        }

        if(!LogicManager.Instance().Start())
        {
            return false;
        }

        _started = true;

        return true;
    }

    public bool Stop()
    {
        if(!_started)
        {
            return false;
        }

        _isPlaying = false;
        _isWaiting = false;
        _started = false;

        LogicManager.Instance().Stop();

        return true;
    }

    public FrameData GetFrameByID(uint id)
    {   
        int index = _turns.FindIndex(delegate(FrameData d){
            return d.FrameId == id;
        });
        if(index != -1)
        {
            return _turns[index];
        }
        return null;
    }

    public bool AddTurn(FrameData frameData)
    {
        if(_turns.FindIndex(delegate(FrameData d){
            return d.FrameId == frameData.FrameId;
        }) != -1)
        {
            return false;
        }

        _turns.Add(frameData);

        _turns.Sort(delegate(FrameData x, FrameData y){
            return (int)(x.FrameId - y.FrameId);
        });

        _maxRecvFrameId = _turns[_turns.Count - 1].FrameId;

        _lastRecvFrameTime = Tools.NowMillisecTime();

        Update();

        return true;
    }

    private bool CheckPlayEnd()
    {
        uint curFrameId = LogicManager.Instance().GetCurFrameId() + 1;

        if(curFrameId >= _maxRecvFrameId)
        {
            _isPlaying = false;
            _updateFrameTime = 0;
            _playGameTick = PLAY_GAME_TICK;

            if(onPlayEnd != null)
            {
                onPlayEnd();
            }
            
            DoUpdate();

            return true;
        }

        return false;
    }

    private void UpdatePlaying()
    {
        long curTime = Tools.NowMillisecTime();
        long subTime = 0 == _updateFrameTime ? _playGameTick : curTime - _updateFrameTime;
        while(subTime >= _playGameTick)
        {
            subTime = subTime - _playGameTick;

            uint curFrameId = LogicManager.Instance().GetCurFrameId() + 1;

            if(CheckPlayEnd())
            {
                return;
            }

            var frame = GetFrameByID(curFrameId);
            if(null == frame)
            {
                Debug.LogError("not find GetFrameByID curFrameId:" + curFrameId);
                return;
            }

            LogicManager.Instance().OnUpdateFrame(FrameHelper.NetFrame2MatchFrame(frame));
        }

        _updateFrameTime = curTime;

        CheckPlayEnd();
    }

    private void DoUpdate()
    {
        long curTime = Tools.NowMillisecTime();

        //uint perfectFrameID = (uint)((curTime - _startTime) / Config.GAME_TICK);

        uint curFrameId = LogicManager.Instance().GetCurFrameId() + 1;

        if(_maxRecvFrameId >= curFrameId)
        {
            _lastUpdateFrameTime = curTime;

            uint count = Math.Min(_maxRecvFrameId - curFrameId + 1,30);
            for(uint i = 0; i < count; i++)
            {
                var frame = GetFrameByID(curFrameId);
                if(null == frame)
                {
                    Debug.LogError("not find GetFrameByID curFrameId:" + curFrameId);
                    return;
                }

                LogicManager.Instance().OnUpdateFrame(FrameHelper.NetFrame2MatchFrame(frame));

                curFrameId++;
            }

            //Debug.Log("update frameId:" + curFrameId);
        }
        else if(_lastRecvFrameTime != 0 && curTime - _lastRecvFrameTime > Config.GAME_TICK * 2)
        {
            _isWaiting = true;

            if(onWaiting != null)
            {
                onWaiting();
            }
        }
    }

    public void Update()
    {
        if(!_started)
        {
            return;
        }

        if(_isWaiting)
        {
            if(LogicManager.Instance().GetCurFrameId() + 1 <= _maxRecvFrameId)
            {
                _isWaiting = false;

                if(onWaitEnd != null)
                {
                    onWaitEnd();
                }

                DoUpdate();
            }
            else
            {
                return;
            }
        }

        if(_isPlaying)
        {
            UpdatePlaying();
            return;
        }

        DoUpdate();
    }
}